package optimizers.ganeat.tictactoe;

import java.awt.GridLayout;
import java.util.HashMap;
import java.util.Map;

import javax.swing.JFrame;
import javax.swing.JPanel;

import model.OligoSystemGeneral;
import model.TicTacToeOligoSystemExplicitMix;

import fitness.FitnessResult;
import fitness.SquareFitness;
import fitness.TicTacToeResult;

import optimizers.commons.RunConfig;
import optimizers.ganeat.Connection;
import optimizers.ganeat.Constants;
import optimizers.ganeat.Individual;
import optimizers.ganeat.Node;
import util.MyUtil;
import visualization.PlotExpData;

public class TicTacToeIndividual extends Individual {

	private TicTacToeOligoSystemExplicitMix ticTacToe;

	public TicTacToeIndividual(Map<String, Integer> innovations,
			Map<String, String> nodeNames, int geneIndex, int indivIndex, RunConfig config) {
		super(innovations, nodeNames, geneIndex, indivIndex, config);
		// TODO Auto-generated constructor stub
	}

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	
	public void evaluateFitness() {
		setTicTacModel(); // dirty fix to get ticTacToe
		double[][] series = ticTacToe.calculateTimeSeries();
		this.fitnessResult = new TicTacToeResult(this.ticTacToe.inputs,this.ticTacToe.correct,series,this.ticTacToe.table[0][0].getDimension(),this.ticTacToe.width,this.ticTacToe.height);
	}
	
	
	protected String getGoalStringName() {
		return "TicTacToe";
	}
	
	//No modification in comparison with getOligoModel, but sets the tictactoe system at the same time
	public void setTicTacModel() {
		Individual miniIndiv = this;
		try {
			miniIndiv = this.getMinimalStructure();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		Map<String, Integer> nameToInt = new HashMap<String, Integer>();
		Map<Integer, String> intToName = new HashMap<Integer, String>();
		int nSimpleSequences = 0;
		int nSequences = 0;
		for (Node node : miniIndiv.nodeGenes) {
			if (node.type == node.SIMPLE_SEQUENCE) {
				nameToInt.put(node.name, nSimpleSequences);
				intToName.put(nSimpleSequences, node.name);
				nSimpleSequences++;
			}
		}
		
		nSequences = nSimpleSequences;
		for (Node node : miniIndiv.nodeGenes) {
			if (node.type == node.INHIBITING_SEQUENCE) {
				boolean foundInhibitedActivation = false;
				for (Connection conn : this.connectionGenes) {
					if (conn.enabled
							&& node.name.equals("I" + conn.from.name
									+ conn.to.name)) {
						foundInhibitedActivation = true;
						break;
					}
				}
				if (foundInhibitedActivation) {
					nameToInt.put(node.name, nSequences);
					intToName.put(nSequences, node.name);
					nSequences++;
				}
			}
		}
		double[][][] template = new double[nSimpleSequences + 1][nSimpleSequences][nSimpleSequences];
		double[][] seqK = new double[nSimpleSequences + 1][nSimpleSequences];
		double[][] seqC = new double[nSimpleSequences + 1][nSimpleSequences];
		double[][] inhK = new double[nSimpleSequences + 1][nSimpleSequences];

		int index;

		// simple sequences K
		for (Node node : miniIndiv.nodeGenes) {
			if (node.type == node.SIMPLE_SEQUENCE) {
				seqK[0][nameToInt.get(node.name)] = node.parameter;
			}
		}

		// simple sequences C
				for (Node node : miniIndiv.nodeGenes) {
					if (node.type == node.SIMPLE_SEQUENCE) {
						seqC[0][nameToInt.get(node.name)] = node.concentration;
					}
				}
		
		// inhibiting sequences K
		for (Node node : miniIndiv.nodeGenes) {
			if (node.type == node.INHIBITING_SEQUENCE) {
				for (Node tmp1 : miniIndiv.nodeGenes) {
					for (Node tmp2 : miniIndiv.nodeGenes) {
						if (node.name.equals("I" + tmp1.name + tmp2.name)) {
							int index1 = nameToInt.get(tmp1.name);
							int index2 = nameToInt.get(tmp2.name);
							inhK[index1 + 1][index2] = MyUtil.calculateInhK(
									seqK[0][index1], seqK[0][index2]);
							seqK[index1 + 1][index2] = inhK[index1 + 1][index2] / 3;
						}
					}
				}
			}
		}

		// Activating Template Concentration
		for (Connection connection : miniIndiv.connectionGenes) {
			if (connection.from.type == Node.SIMPLE_SEQUENCE
					&& connection.to.type == Node.SIMPLE_SEQUENCE
					&& connection.enabled) {
				int index1 = nameToInt.get(connection.from.name);
				int index2 = nameToInt.get(connection.to.name);
				template[0][index1][index2] = connection.parameter;
			}
		}

		// Inhibiting Template Concentration
		for (Connection connection : miniIndiv.connectionGenes) {
			if (connection.from.type == Node.SIMPLE_SEQUENCE
					&& connection.to.type == Node.INHIBITING_SEQUENCE
					&& connection.enabled
					&& nameToInt.get(connection.to.name) != null) {
				for (Node tmp1 : this.nodeGenes) {
					for (Node tmp2 : this.nodeGenes) {
						if (connection.to.name.equals("I" + tmp1.name
								+ tmp2.name)) {
							index = nameToInt.get(connection.from.name);
							int index1 = nameToInt.get(tmp1.name);
							int index2 = nameToInt.get(tmp2.name);
							template[index + 1][index1][index2] = connection.parameter;
						}
					}
				}
			}
		}
		
		this.ticTacToe = new TicTacToeOligoSystemExplicitMix(nSimpleSequences, template, seqK, inhK,
				seqC, exo);
	}
	
	@Override
	protected Node getFirstRedundantNode() {
		for (Node node : nodeGenes) {
			if(node.name != "w" && node.name != "v" && node.name != "a" && node.name != "E" && node.name != "b"){
				boolean foundConnectionTo = false;
				for (Connection conn : connectionGenes) {
					if (conn.enabled && conn.to == node) {
						foundConnectionTo = true;
						break;
					}
				}
				if (!foundConnectionTo) {
					return node;
				}
			}
		}
		return null;
	}
	
	@Override
	public JPanel displayTimeSereis() {
		JPanel panel = new JPanel();
		//frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		panel.setSize(800, 600);
		//double time1, time2;
		//time1 = System.currentTimeMillis();
		//System.out.println("Knock knock");
		double[][] time = ticTacToe.calculateTimeSeries();
		//time2 = System.currentTimeMillis();
		//System.out.println("done in "+(time2-time1));
		panel.setLayout(new GridLayout(3,3));
		//System.out.println("Who's there?");
		double[][] separated = new double[ticTacToe.table[0][0].total][];
		for(int i=0; i<ticTacToe.width; i++){
			for(int j=0; j<ticTacToe.height; j++){
				for(int k=0; k<ticTacToe.table[0][0].total; k++){
					separated[k] = time[ticTacToe.table[0][0].total*(i*3+j)+k];
				}
				panel.add(new PlotExpData("TicTac"+(i*3+j),separated.clone()));
			}
		}
		return panel;
	}

}
